﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text;
using System.Xml;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;

namespace Athena.Objects
{
    [Serializable()]
    public class Decorator : IDrawable 
    {
        #region Point / Dimensions / Bounds

        protected PointF _Location = new PointF();

        public override IDrawable AnchoredSource
        {
            get
            {
                return base.AnchoredSource;
            }
            set
            {
                base.AnchoredSource = value;
                if (value != null)
                    Parent = value.Parent;
                DoneModifying(Corner.None, false);
            }
        }

        public override PointF Location
        {
            get
            {
                return _Location;
            }
            set
            {
                _Location = value;

            }
        }
        public override void MoveTo(PointF value)
        {            
            if (ParentGlyph == null)
                _Location = value;
            else
            {
                PointF oTemp = new PointF(value.X + _Dimensions.Width / 2f, value.Y + _Dimensions.Height / 2f);
                float fDistance = 0f;//Math.Min(Dimensions.Height, Dimensions.Width) / 2f;
                PointF oCenter = Utility.GetPointOnEdge(ParentGlyph.Bounds, oTemp, fDistance);
                _Location = new PointF(oCenter.X - _Dimensions.Width / 2f, oCenter.Y - _Dimensions.Height / 2f);                
            }
        }
        public override void DoneModifying(Corner oCorner, bool modifyAnchoredItems)
        {
            PointF oTemp = Center;            
            float fDistance = 0f;//Math.Min(Dimensions.Height, Dimensions.Width) / 2f;
            PointF oCenter = Utility.GetPointOnEdge(ParentGlyph.Bounds, oTemp, fDistance);
            _Location = new PointF(oCenter.X - Dimensions.Width / 2f, oCenter.Y - Dimensions.Height / 2f);

            base.DoneModifying(oCorner, modifyAnchoredItems);
        }

        protected SizeF _Dimensions;
        public override SizeF Dimensions
        {
            get
            {
                return _Dimensions;
            }
            set
            {
                if (_oLabelBounds.X > _Dimensions.Width / 2)
                {
                    _oLabelBounds.X += (value.Width - _Dimensions.Width);
                }
                if (_oLabelBounds.Y > _Dimensions.Height / 2)
                {
                    _oLabelBounds.Y += (value.Height - _Dimensions.Height);
                }
                _Dimensions = value;
            }
        }

        public override RectangleF Bounds
        {
            get
            {
                return new RectangleF(_Location, _Dimensions);
            }
            set
            {
                _Location = value.Location;
                _Dimensions = value.Size;
            }
        }

        #endregion

        #region Select / Deselect

        protected bool _IsSelected;
        public override bool IsSelected
        {
            get
            {
                return _IsSelected;
            }
            set
            {
                _IsSelected = value;
            }
        }
        public override void Select() { _IsSelected = true; }
        public override void Deselect() { _IsSelected = false; }

        #endregion

        #region Transform / Where

        public override void Transform(System.Drawing.PointF oDirection, ref Corner oCorner, bool Ctrl)
        {
            if (Ctrl)
            {
                _Dimensions.Width = _Dimensions.Height = (_Dimensions.Width + _Dimensions.Height) / 2f;
                float avg = (Math.Abs(oDirection.X) + Math.Abs(oDirection.Y)) / 2f;
                if (oDirection.X > 0 && oDirection.Y > 0)
                    oDirection = new PointF(avg, avg);
                else
                {
                    if (oDirection.X < 0 && oDirection.Y < 0)
                        oDirection = new PointF(-avg, -avg);
                    else
                    {
                        if (oDirection.X < 0)
                            oDirection = new PointF(-avg, avg);
                        else
                            oDirection = new PointF(avg, -avg);
                    }
                }
            }
            switch (oCorner)
            {
                case Corner.BottomLeft:
                    {
                        _Location.X += oDirection.X;
                        _Dimensions.Width -= oDirection.X;

                        if (_oLabelBounds.Y > _Dimensions.Height / 2)
                        {
                            _oLabelBounds.Y += oDirection.Y;
                        }

                        _Dimensions.Height += oDirection.Y;
                        break;
                    }
                case Corner.BottomRight:
                    {
                        if (_oLabelBounds.X > _Dimensions.Width / 2)
                        {
                            _oLabelBounds.X += oDirection.X;
                        }
                        if (_oLabelBounds.Y > _Dimensions.Height / 2)
                        {
                            _oLabelBounds.Y += oDirection.Y;
                        }
                        _Dimensions.Width += oDirection.X;
                        _Dimensions.Height += oDirection.Y;
                        break;
                    }
                case Corner.TopLeft:
                    {
                        _Location.X += oDirection.X;
                        _Location.Y += oDirection.Y;
                        _Dimensions.Width -= oDirection.X;
                        _Dimensions.Height -= oDirection.Y;
                        break;
                    }
                case Corner.TopRight:
                    {
                        if (_oLabelBounds.X > _Dimensions.Width / 2)
                        {
                            _oLabelBounds.X += oDirection.X;
                        }
                        _Location.Y += oDirection.Y;
                        _Dimensions.Width += oDirection.X;
                        _Dimensions.Height -= oDirection.Y;
                        break;
                    }

                default:
                    break;
            }

            if (_Dimensions.Height < 0)
            {
                _Dimensions.Height = 0;
                oCorner = FlipCornerY(oCorner);
            }
            if (_Dimensions.Width < 0)
            {
                _Dimensions.Width = 0;
                oCorner = FlipCornerX(oCorner);
            }

        }
        public Corner FlipCornerY(Corner oCorner)
        {
            switch (oCorner)
            {
                case Corner.BottomLeft:
                    return Corner.TopLeft;
                case Corner.BottomRight:
                    return Corner.TopRight;
                case Corner.TopLeft:
                    return Corner.BottomLeft;
                case Corner.TopRight:
                    return Corner.BottomRight;
                default:
                    return Corner.None;
            }
        }
        public Corner FlipCornerX(Corner oCorner)
        {
            switch (oCorner)
            {
                case Corner.BottomLeft:
                    return Corner.BottomRight;
                case Corner.BottomRight:
                    return Corner.BottomLeft;
                case Corner.TopLeft:
                    return Corner.TopRight;
                case Corner.TopRight:
                    return Corner.TopLeft;
                default:
                    return Corner.None;
            }
        }
        public override Corner Where(RectangleF oRect)
        {
            RectangleF oBounds = Bounds;
            oBounds.Inflate(5, 5);
            RectangleF oTopLeft = new RectangleF(oBounds.Left - 3F, oBounds.Top - 3F, 6F, 6F);
            RectangleF oTopRight = new RectangleF(oBounds.Right - 3F, oBounds.Top - 3F, 6F, 6F);
            RectangleF oBottomLeft = new RectangleF(oBounds.Left - 3F, oBounds.Bottom - 3F, 6F, 6F);
            RectangleF oBottomRight = new RectangleF(oBounds.Right - 3F, oBounds.Bottom - 3F, 6F, 6F);

            if (oTopLeft.IntersectsWith(oRect)) return Corner.TopLeft;
            if (oTopRight.IntersectsWith(oRect)) return Corner.TopRight;
            if (oBottomLeft.IntersectsWith(oRect)) return Corner.BottomLeft;
            if (oBottomRight.IntersectsWith(oRect)) return Corner.BottomRight;

            return Corner.None;
        }
        public override Corner Where(PointF oPoint)
        {
            RectangleF oBounds = Bounds;
            oBounds.Inflate(5, 5);
            RectangleF oTopLeft = new RectangleF(oBounds.Left - 5F, oBounds.Top - 5F, 10F, 10F);

            RectangleF oTopRight = new RectangleF(oBounds.Right - 5F, oBounds.Top - 5F, 10F, 10F);

            RectangleF oBottomLeft = new RectangleF(oBounds.Left - 5F, oBounds.Bottom - 5F, 10F, 10F);

            RectangleF oBottomRight = new RectangleF(oBounds.Right - 5F, oBounds.Bottom - 5F, 10F, 10F);


            if (oTopLeft.Contains(oPoint)) return Corner.TopLeft;
            if (oTopRight.Contains(oPoint)) return Corner.TopRight;
            if (oBottomLeft.Contains(oPoint)) return Corner.BottomLeft;
            if (oBottomRight.Contains(oPoint)) return Corner.BottomRight;
            return Corner.None;
        }

        #endregion

        #region Edge Color / Thickness

        protected float _dEdgeThickness = 2f;
        [CategoryAttribute("IGlyph Attributes"), Description("Thickness of the Edge for the Object.")]
        public float EdgeThickness
        {
            get { return _dEdgeThickness; }
            set { _dEdgeThickness = value; }
        }

        protected Color _oEdgeColor = Color.Black;

        [CategoryAttribute("IGlyph Attributes"), Description("Color of the Object Outline.")]
        public Color EdgeColor
        {
            get { return _oEdgeColor; }
            set { _oEdgeColor = value; }
        }
        #endregion

        #region Label Color / Font / Bounds
                

        protected RectangleF _oLabelBounds = new RectangleF();
        protected bool _bLabelBoundsChanged = false;
        [System.ComponentModel.CategoryAttribute("IGlyph Attributes"),
        System.ComponentModel.DescriptionAttribute("Bounds of the Label, Position is relative to the position of the glyph."),
    ReadOnlyAttribute(false), TypeConverter(typeof(RectangleFConverter))]

        public RectangleF LabelBounds
        {
            get { return _oLabelBounds; }
            set
            {
                _bLabelBoundsChanged = true;
                _oLabelBounds = value;
            }
        }
                
        #endregion

        #region Parent Glyph

        //private IGlyph _ParentGlyph = null;
        public IGlyph ParentGlyph
        {
            get { return _AnchoredSource as IGlyph; }
            set { if (value is IGlyph) AnchoredSource = value; }
        }
        #endregion

        public enum DecoratorStyle
        {            
            Circle,
            LongCircle,
            Ellipse, 
            Rectangle
        }

        protected DecoratorStyle _Style = DecoratorStyle.Circle;
        public DecoratorStyle Style
        {
            get { return _Style; }
            set { _Style = value; }
        }


        public override object Clone()
        {
            Decorator oResult = new Decorator();
            oResult.Bounds = Bounds;

            oResult.ShowText = ShowText; 
            oResult.TextV = TextV;
            oResult.TextH = TextH;
            oResult.Text = Text;
            oResult.TextFont = TextFont;

            oResult.ColorStart = ColorStart;
            oResult.ColorEnd = ColorEnd;
            
            oResult.EdgeColor = EdgeColor;
            oResult.EdgeThickness = EdgeThickness;

            oResult.Style = Style;
            
            oResult.ParentGlyph = ParentGlyph;

            return oResult;
        }

        public virtual void DrawLabel(Graphics g)
        {
            if (_oLabelFont == null)
            {
                return;
            }
            if (Dimensions.Height > 0)
            {



                RectangleF oLabelBounds = Bounds;
                Font oLabelFont = null;
                //if (_bLabelChanged)
                //{
                oLabelFont = _oLabelFont;
                /*}
                else
                {
                    oLabelFont = new Font(
                        _oLabelFont.FontFamily,
                        oLabelBounds.Height / 4f,
                        _oLabelFont.Style,
                        _oLabelFont.Unit,
                        _oLabelFont.GdiCharSet,
                        _oLabelFont.GdiVerticalFont);
                }*/

                SizeF oSize = g.MeasureString(Text, oLabelFont, int.MaxValue);

                //if (oSize.Width > Bounds.Width && LabelH != HorizontalAlignment.LeftOfLeft && LabelH != HorizontalAlignment.RightOfRight)
                //{
                //    LabelV = VerticalAlignment.BelowBottom;
                //    //oLabelBounds.Width += oSize.Width + oSize.Width / 10f;
                //}

                //if (oSize.Height > Bounds.Height && LabelV != VerticalAlignment.AboveTop && LabelV != VerticalAlignment.BelowBottom)
                //    LabelV = VerticalAlignment.BelowBottom;

                switch (TextH)
                {
                    case HorizontalAlignment.LeftOfLeft:
                        oLabelBounds.X = oLabelBounds.X - oSize.Width - oSize.Width / 10f;
                        oLabelBounds.Width += oSize.Width + oSize.Width / 10f;
                        break;
                    case HorizontalAlignment.RightOfRight:
                        oLabelBounds.X = oLabelBounds.X + oLabelBounds.Width + oSize.Width / 10f;
                        oLabelBounds.Width += oSize.Width + oSize.Width / 10f;
                        break;
                    case HorizontalAlignment.Left:
                        oLabelBounds.X = oLabelBounds.X;
                        break;
                    case HorizontalAlignment.Center:
                        oLabelBounds.X = oLabelBounds.X + oLabelBounds.Width / 2f - oSize.Width / 2f;
                        break;
                    case HorizontalAlignment.Right:
                        oLabelBounds.X = oLabelBounds.X + oLabelBounds.Width - oSize.Width;
                        break;
                    default:
                    case HorizontalAlignment.None:
                        {
                            if (_bLabelBoundsChanged)
                            {
                                oLabelBounds = _oLabelBounds;
                                oLabelBounds.Location = Utility.AddPoints(_oLabelBounds.Location, Bounds.Location);
                            }
                            else
                            {

                                oLabelBounds.Width = Bounds.Width;
                                oLabelBounds.Height = Bounds.Height / 2f;
                            }
                            break;
                        }
                }





                switch (TextV)
                {

                    case VerticalAlignment.AboveTop:
                        oLabelBounds.Y = oLabelBounds.Y - oSize.Height - oSize.Height * 0.1f;
                        oLabelBounds.Height += oSize.Height + oSize.Height * 0.1f;
                        oLabelBounds.Width += oSize.Width + oSize.Width / 10f;
                        break;
                    case VerticalAlignment.BelowBottom:
                        oLabelBounds.Y = oLabelBounds.Y + oLabelBounds.Height + oSize.Height * 0.1f;
                        oLabelBounds.Width += oSize.Width + oSize.Width / 10f;
                        oLabelBounds.Height += oSize.Height + oSize.Height * 0.1f;
                        break;
                    case VerticalAlignment.Top:
                        oLabelBounds.Y = oLabelBounds.Y;
                        break;
                    case VerticalAlignment.Middle:
                        oLabelBounds.Y = oLabelBounds.Y + oLabelBounds.Height / 2f - oSize.Height / 2f;
                        break;
                    case VerticalAlignment.Bottom:
                        oLabelBounds.Y = oLabelBounds.Y + oLabelBounds.Height - oSize.Height;
                        break;
                    default:
                    case VerticalAlignment.None:
                        break;
                }





                if (!_bLabelBoundsChanged && TextV == VerticalAlignment.None && TextH == HorizontalAlignment.None)
                {
                    oLabelBounds.X += (oLabelBounds.Width - oSize.Width) / 2f;
                    oLabelBounds.Y += oSize.Height / 8f;
                    //oLabelBounds.Y += (oLabelBounds.Height - oSize.Height) / 2f;
                    oLabelBounds.Width = oSize.Width;
                    oLabelBounds.Height = oSize.Height;
                }

                g.DrawString(Text, oLabelFont, new SolidBrush(_oLabelColor), oLabelBounds);
            }

        }

        public override void Draw(System.Drawing.Graphics g)
        {

            if (!IsVisible) return;

            Pen edgePen = new Pen(EdgeColor, EdgeThickness);
            Brush edgeBrush = new SolidBrush(EdgeColor);
            //Brush backgroundBrush = new SolidBrush(Program.Model.Background.StartColor);
            Brush backgroundBrush = Brushes.White;


            switch (Style)
            {
                case DecoratorStyle.Circle:
                    g.FillEllipse(backgroundBrush, Bounds);
                    g.DrawEllipse(edgePen, Bounds);
                    break;
                case DecoratorStyle.Ellipse:
                    g.FillEllipse(backgroundBrush, Bounds);
                    g.DrawEllipse(edgePen, Bounds);
                    break;
                case DecoratorStyle.LongCircle:
                    {
                        if (Bounds.Width > Bounds.Height)
                        {
                            GraphicsPath oPath = new GraphicsPath();
                            oPath.StartFigure();
                            
                            RectangleF leftArc = new RectangleF(Bounds.X, Bounds.Y, Bounds.Height, Bounds.Height);
                            RectangleF rightArc = new RectangleF(Bounds.Width + Bounds.X - Bounds.Height, Bounds.Y, Bounds.Height, Bounds.Height);

                            oPath.AddArc(leftArc, 90f, 180f);
                            oPath.AddLine(Bounds.X + Bounds.Height / 2f, Bounds.Y, Bounds.X + Bounds.Width - Bounds.Height , Bounds.Y);
                            oPath.AddArc(rightArc, 270f, 180f);
                            oPath.AddLine(Bounds.X + Bounds.Width - Bounds.Height, Bounds.Y + Bounds.Height, Bounds.X + Bounds.Height / 2f, Bounds.Height + Bounds.Y);

                            oPath.CloseFigure();

                            g.FillPath(backgroundBrush, oPath);
                            g.DrawPath(edgePen, oPath);
                        }
                        else
                        {
                            g.FillEllipse(backgroundBrush, Bounds);
                            g.DrawEllipse(edgePen, Bounds);
                        }
                        break;
                    }
                case DecoratorStyle.Rectangle:
                    g.FillRectangle(backgroundBrush, Bounds);
                    g.DrawRectangle(edgePen, Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height);
                    break;
                default:
                    break;
            }

            DrawLabel(g);   

            if (_IsSelected)
            {
                RectangleF oBounds = Bounds;

                oBounds.Inflate(2.5F, 2.5F);

                g.DrawLine(Pens.Red, oBounds.Location.X + 3, oBounds.Location.Y, oBounds.Location.X + oBounds.Size.Width - 3, oBounds.Location.Y);
                g.DrawLine(Pens.Red, oBounds.Location.X + 3, oBounds.Location.Y + oBounds.Height, oBounds.Location.X + oBounds.Width - 3, oBounds.Location.Y + oBounds.Height);
                g.DrawLine(Pens.Red, oBounds.Location.X, oBounds.Location.Y + 3, oBounds.Location.X, oBounds.Location.Y + oBounds.Height - 3);
                g.DrawLine(Pens.Red, oBounds.Location.X + oBounds.Width, oBounds.Location.Y + 3, oBounds.Location.X + oBounds.Width, oBounds.Location.Y + oBounds.Height - 3);

                g.DrawEllipse(Pens.Red, oBounds.Left - 3, oBounds.Top - 3, 6, 6);
                g.DrawEllipse(Pens.Red, oBounds.Right - 3, oBounds.Top - 3, 6, 6);
                g.DrawEllipse(Pens.Red, oBounds.Left - 3, oBounds.Bottom - 3, 6, 6);
                g.DrawEllipse(Pens.Red, oBounds.Right - 3, oBounds.Bottom - 3, 6, 6);
            }

        }

        public override void ShowToolTip(ToolTip tooltip, Control pnlCanvas, PointF location)
        {
            tooltip.ToolTipTitle = "Decorator: ";
            tooltip.SetToolTip(pnlCanvas,
                String.Format(
                            "{1}Label:\t{0}",
                _sLabel,
                "\r\n"));
        }

        /// <summary>
        /// writes the iglyph attributes to xml
        /// </summary>
        /// <param name="writer"></param>
        public override void WriteXML(System.Xml.XmlWriter writer)
        {
            base.WriteXML(writer);

            writer.WriteStartElement("Decorator", "");

            writer.WriteStartElement("EdgeColor");
            writer.WriteValue(_oEdgeColor.Name);
            writer.WriteEndElement();

            writer.WriteStartElement("LabelWidth");
            writer.WriteValue(_oLabelBounds.Width);
            writer.WriteEndElement();

            writer.WriteStartElement("LabelHeight");
            writer.WriteValue(_oLabelBounds.Height);
            writer.WriteEndElement();

            writer.WriteStartElement("Style");
            writer.WriteValue((int)(_Style - DecoratorStyle.Circle));
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
        /// <summary>
        /// reads the iglyph attributes from xml
        /// </summary>
        /// <param name="writer"></param>
        public override bool ReadXML(XmlReader reader)
        {
            bool read = base.ReadXML(reader);
            read = reader.Read();

            if (reader.NodeType == XmlNodeType.Element && reader.Name == "Decorator")
            {
                read = reader.Read();
                while (read)
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;

                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "EdgeColor":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oEdgeColor = Color.FromName(reader.ReadContentAsString());

                                break;
                            case "LabelWidth":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    _oLabelBounds = new RectangleF(_oLabelBounds.X, _oLabelBounds.Y,
                                                                   reader.ReadContentAsFloat(), _oLabelBounds.Height);
                                }

                                break;
                            case "LabelHeight":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    _oLabelBounds = new RectangleF(_oLabelBounds.X, _oLabelBounds.Y,
                                                                   _oLabelBounds.Width, reader.ReadContentAsFloat());
                                }

                                break;
                            case "Style":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                {
                                    if (reader.NodeType == XmlNodeType.Text)
                                        _Style = DecoratorStyle.Circle + reader.ReadContentAsInt();
                                }
                                break;
                            default:
                                while (reader.NodeType != XmlNodeType.EndElement)
                                    read = reader.Read();
                                break;
                        }
                    }

                    if (!read || (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Decorator")) break;

                    if (reader.NodeType != XmlNodeType.Element)
                        read = reader.Read();
                }
                return true;
            }
            return false;
        }
    }
}
